home *** CD-ROM | disk | FTP | other *** search
/ Mac Power 1997 January / macpower199701.bin / AMUG / Programming_10 / WASTE 1.3a1.sit / WASTE 1.3a1 Distribution / WASTE 1.3a1 / WESelecting.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-23  |  55.6 KB  |  2,146 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WESelecting.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Drawing Selections, Activating, Updating, Scrolling, etc.
  6.  *
  7.  *  Copyright (c) 1993-1996 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. // values for _WEArrowOffset action parameter:
  18.  
  19. enum {
  20.  
  21. // plain arrow keys
  22.     kGoLeft            =    0,
  23.     kGoRight        =    1,
  24.     kGoUp            =    2,
  25.     kGoDown            =    3,
  26.  
  27. // modifiers
  28.     kOption            =    4,
  29.     kCommand        =    8,
  30.  
  31. // option + arrow combos
  32.     kGoWordStart    =    kGoLeft + kOption,
  33.     kGoWordEnd        =    kGoRight + kOption,
  34.     kGoTextStart    =    kGoUp + kOption,
  35.     kGoTextEnd        =    kGoDown + kOption,
  36.  
  37. // command + arrow combos
  38.     kGoLineStart    =    kGoLeft + kCommand,
  39.     kGoLineEnd        =    kGoRight + kCommand,
  40.     kGoPageStart    =    kGoUp + kCommand,
  41.     kGoPageEnd        =    kGoDown + kCommand
  42. };
  43.  
  44. INLINE pascal void _WEClearHiliteBit(void)
  45. {
  46.     LMSetHiliteMode(LMGetHiliteMode() & 0x7F);
  47. }
  48.  
  49. static Boolean SLPixelToChar(LinePtr pLine, const WERunAttributes *pAttrs,
  50.         Ptr pSegment, SInt32 segmentStart, SInt32 segmentLength,
  51.         JustStyleCode styleRunPosition, WEHandle hWE, void *callbackData)
  52. {
  53.     struct SLPixelToCharData *cd = (struct SLPixelToCharData *) callbackData;
  54.     WEPtr pWE = *hWE;
  55.     Fixed slop;
  56.     SInt16 cType;
  57.     Fixed oldWidth;
  58. #if WASTE_OBJECTS
  59.     Fixed objectWidth;
  60.     Fixed subWidth;
  61. #endif
  62.  
  63.     // if this is the first style run on the line, subtract pen indent from pixelWidth
  64.     if (IS_LEFTMOST_RUN(styleRunPosition))
  65.     {
  66.         cd->pixelWidth -= BSL(_WECalcPenIndent(pLine, pWE->alignment, pWE->direction), 16);
  67.     }
  68.  
  69.     // if pixelWidth is gone negative already, the point is on the trailing edge of first glyph
  70.     if (cd->pixelWidth <= 0)
  71.     {
  72.         cd->offset = segmentStart;
  73.         cd->edge = kLeadingEdge;
  74.         return true;    // stop looping
  75.     }
  76.  
  77.     oldWidth = cd->pixelWidth;
  78.  
  79. #if WASTE_OBJECTS
  80.     if (pAttrs->runStyle.tsObject != nil)
  81.     {
  82.  
  83.         // EMBEDDED OBJECT
  84.         // calculate object width as Fixed
  85.         objectWidth = BSL((*pAttrs->runStyle.tsObject)->objectSize.h, 16);
  86.  
  87.         // subtract object width from pixelWidth
  88.         cd->pixelWidth -= objectWidth;
  89.  
  90. #if WASTE_OBJECTS_ARE_GLYPHS
  91.  
  92.         // find out whether the point is in the leftmost half of the object,
  93.         // in the rightmost half or past the object
  94.         subWidth = objectWidth >> 1;    // divide by two
  95.         if (cd->pixelWidth + subWidth < 0)
  96.         {
  97.             cd->offset = segmentStart;
  98.             cd->edge = kLeadingEdge;        // point is in leftmost half of object
  99.         }
  100.         else
  101.         {
  102.             cd->offset= segmentStart + 1;
  103.             if (cd->pixelWidth < 0)
  104.                 cd->edge = kTrailingEdge;    // point is in rightmost half of object
  105.             else
  106.                 cd->edge = kLeadingEdge;    // point is past object
  107.         }
  108. #else
  109.  
  110.         // find out whether the point is in the leftmost quarter of the object,
  111.         // in the middle half, in the rightmost quarter or past the object
  112.         subWidth = objectWidth >> 2;    // divide by four
  113.         if (cd->pixelWidth + subWidth < 0)
  114.         {
  115.             cd->offset = segmentStart;
  116.             if (cd->pixelWidth + objectWidth < subWidth)
  117.                 cd->edge = kLeadingEdge;        // point is in leftmost quarter of object
  118.             else
  119.                 cd->edge = kObjectEdge;        // point is in middle half of object
  120.         }
  121.         else
  122.         {
  123.             cd->offset = segmentStart + 1;
  124.             if (cd->pixelWidth < 0)
  125.                 cd->edge = kTrailingEdge;    // point is in rightmost quarter of object
  126.             else
  127.                 cd->edge = kLeadingEdge;        // point is past object
  128.         }
  129. #endif // WASTE_OBJECTS_ARE_GLYPHS
  130.     }
  131.     else
  132. #endif // WASTE_OBJECTS
  133.     {
  134.  
  135.         // REGULAR TEXT
  136.  
  137.         // if this is the last segment on the line, strip the last blank character (if any),
  138.         // unless it is the last non-CR character in the whole text stream
  139.         if (IS_RIGHTMOST_RUN(styleRunPosition))
  140.         {
  141.             if ((segmentStart + segmentLength < pWE->textLength) ||
  142.                 pSegment[segmentLength - 1] == kEOL)
  143.             {
  144.                 cType = CallWECharTypeProc(pSegment, segmentLength - 1, FontScript(),
  145.                      hWE, pWE->charTypeHook);
  146.                 if ((cType & (smcTypeMask + smcClassMask)) == smCharPunct + smPunctBlank)
  147.                 {
  148.                     segmentLength -= ((cType & smcDoubleMask) ? 2 : 1);
  149.                 }
  150.             }
  151.         }
  152.  
  153.         // calculate slop for this text segment (justified text only)
  154.         if (pWE->alignment == weJustify)
  155.         {
  156.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
  157.                     kOneToOneScaling, kOneToOneScaling), pLine->lineJustAmount);
  158.         }
  159.         else
  160.         {
  161.             slop = 0;
  162.         }
  163.  
  164.         // call PixelToChar hook for this segment
  165.         cd->offset = segmentStart + CallWEPixelToCharProc(pSegment, segmentLength,
  166.                 slop, &cd->pixelWidth, &cd->edge, styleRunPosition, cd->hPos,
  167.                 hWE, pWE->pixelToCharHook);
  168.     }
  169.  
  170.     // increment hPos by change in pixelWidth
  171.     cd->hPos += (oldWidth - cd->pixelWidth);
  172.  
  173.     // if pixelWidth has gone negative, we're finished; otherwise go to next run
  174.     return (cd->pixelWidth <= 0);
  175. }
  176.  
  177. pascal SInt32 WEGetOffset(const LongPt *thePoint, WEEdge *edge, WEHandle hWE)
  178. {
  179.     // given a long point in local coordinates,
  180.     // find the text offset corresponding to the nearest glyph
  181.  
  182.     WEPtr pWE;
  183.     SInt32 lineIndex;
  184.     LongPt tempPoint;
  185.     struct SLPixelToCharData cd;
  186.     Boolean saveWELock;
  187.  
  188.     // lock the WE record
  189.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  190.     pWE = *hWE;
  191.  
  192.     // tempPoint is thePoint, relative to the top left corner of the dest rect
  193.     tempPoint.v = thePoint->v - pWE->destRect.top;
  194.     tempPoint.h = thePoint->h - pWE->destRect.left;
  195.  
  196.     // if the point is above the destination rect, return zero
  197.     if (tempPoint.v < 0)
  198.     {
  199.         cd.offset = 0;
  200.         cd.edge = kTrailingEdge;
  201.     }
  202.     else
  203.     {
  204.         // if the point is below the last line, return last char offset
  205.         if (tempPoint.v >= WEGetHeight(0, LONG_MAX, hWE))
  206.         {
  207.             cd.offset = pWE->textLength;
  208.             cd.edge = kLeadingEdge;
  209.         }
  210.         else
  211.         {
  212.             // find the line index corresponding to the vertical pixel offset
  213.             lineIndex = _WEPixelToLine(tempPoint.v, hWE);
  214.  
  215.             // express the horizontal pixel offset as a Fixed value
  216.             cd.pixelWidth = BSL(tempPoint.h, 16);
  217.  
  218.             // walk through the segments on this line calling PixelToChar
  219.             cd.hPos = 0;
  220.             cd.offset = 0;
  221.             _WESegmentLoop(lineIndex, lineIndex, SLPixelToChar, &cd, hWE);
  222.         }
  223.     }
  224.  
  225.     // unlock the WE record
  226.     _WESetHandleLock((Handle) hWE, saveWELock);
  227.  
  228.     // return offset/edge pair
  229.     if (edge != nil)
  230.     {
  231.         *edge = cd.edge;
  232.     }
  233.     return cd.offset;
  234. }
  235.  
  236. static Boolean SLCharToPixel(LinePtr pLine, const WERunAttributes *pAttrs,
  237.         Ptr pSegment, SInt32 segmentStart, SInt32 segmentLength,
  238.         JustStyleCode styleRunPosition, WEHandle hWE, void *callbackData)
  239. {
  240.     struct SLCharToPixelData *cd = (struct SLCharToPixelData *) callbackData;
  241.     WEPtr pWE = *hWE;
  242.     SInt32 offset;
  243.     Fixed slop;
  244.     SInt16 direction = cd->direction;
  245.     SInt16 width;
  246.     Boolean isInSegment;
  247.  
  248.     // calculate offset relative to beginning of segment
  249.     offset = cd->offset - segmentStart;
  250.  
  251.     // is offset within this segment?
  252.     isInSegment = ((offset >= 0) && (offset < segmentLength));
  253.  
  254.     // if this is the first style run on the line, add pen indent to thePoint.h
  255.     if (IS_LEFTMOST_RUN(styleRunPosition))
  256.     {
  257.         cd->thePoint->h += _WECalcPenIndent(pLine, pWE->alignment, pWE->direction);
  258.     }
  259.  
  260. #if WASTE_OBJECTS
  261.     if (pAttrs->runStyle.tsObject != nil)
  262.     {
  263.  
  264.         // EMBEDDED OBJECT
  265.         width = isInSegment ? 0 : (*pAttrs->runStyle.tsObject)->objectSize.h;
  266.     }
  267.     else
  268. #endif
  269.     {
  270.         // REGULAR TEXT
  271.         slop = 0;
  272.  
  273.         // calculate slop for this text segment (justified text only)
  274.         if (pWE->alignment == weJustify)
  275.         {
  276.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
  277.                 kOneToOneScaling, kOneToOneScaling), pLine->lineJustAmount);
  278.         }
  279.  
  280.         if (BTST(pWE->flags, weFBidirectional))
  281.         {
  282.             //    SPECIAL TREATMENT FOR BIDIRECTIONAL SCRIPTS
  283.             Boolean    segmentDir = (BTST(pAttrs->runStyle.tsFlags, tsRightToLeft) != 0);
  284.  
  285.             if ((offset < 0) || (offset > segmentLength))
  286.             {
  287.                 // we want the total width of this segment, so set offset and direction appropriately
  288.                 if (segmentDir)
  289.                 {
  290.                     offset = 0;
  291.                     direction = rightCaret;
  292.                 }
  293.                 else
  294.                 {
  295.                     offset = segmentLength;
  296.                     direction = leftCaret;
  297.                 }
  298.             }
  299.             else if (cd->rightEdge)
  300.             {
  301.                 // we're drawing a cursor at the right edge of the line, so ensure we measure this whole segment
  302.                 isInSegment = false;
  303.                 if (cd->lineDir && !segmentDir)
  304.                 {
  305.                     offset = segmentLength;
  306.                     direction = leftCaret;
  307.                 }
  308.                 else if (!cd->lineDir && segmentDir)
  309.                 {
  310.                     offset = 0;
  311.                     direction = rightCaret;
  312.                 }
  313.             }
  314.             else if ((offset == 0) || (offset == segmentLength))
  315.             {
  316.                 isInSegment = false;
  317.                 // we're at a style run boundary
  318.                 if (((direction == leftCaret) && (! segmentDir)) || ((direction == rightCaret) && segmentDir))
  319.                 {
  320.                     // requested caret direction matches style run direction, so we can exit the loop
  321.                     isInSegment = true;
  322.                 }
  323.                 else
  324.                 {
  325.                     // need caret for direction opposite to this run, so need to check for direction run boundary;
  326.                     // determine whether preceding and following runs are R-L
  327.                     Boolean prevDir;
  328.                     Boolean nextDir;
  329.  
  330.                     //    note that if segmentStart is 0, WEGetRunDirection
  331.                     //    will return the primary line direction
  332.                     prevDir = WEGetRunDirection(segmentStart - 1, hWE);
  333.  
  334.                     //    again, if segmentStart + segmentLength == pWE->textLength,
  335.                     //    WEGetRunDirection will do the Right Thing
  336.                     nextDir = WEGetRunDirection(segmentStart + segmentLength, hWE);
  337.  
  338.                     if ((offset == 0) && (prevDir != segmentDir))
  339.                     {
  340.                         if (cd->lineDir)
  341.                         {
  342.                             if (segmentDir)
  343.                             {
  344.                                 //offset = 0;    // already known to be zero
  345.                                 direction = rightCaret;
  346.                             }
  347.                             else
  348.                             {
  349.                                 offset = segmentLength;
  350.                                 direction = leftCaret;
  351.                             }
  352.                         }
  353.                     }
  354.                     else if ((offset == segmentLength) && (nextDir != segmentDir))
  355.                     {
  356.                         if (!cd->lineDir)
  357.                         {
  358.                             if (segmentDir)
  359.                             {
  360.                                 offset = 0;
  361.                                 direction = rightCaret;
  362.                             }
  363.                             else
  364.                             {
  365.                                 //offset = segmentLength;
  366.                                 direction = leftCaret;
  367.                             }
  368.                         }
  369.                     }
  370.                     else
  371.                     {
  372.                         // not a direction boundary, so force caret dir to match style run dir, and exit loop
  373.                         direction = segmentDir ? rightCaret : leftCaret;
  374.                         isInSegment = true;
  375.                     }
  376.                 }
  377.             }
  378.         } // end of special treatment for bidirectional scripts
  379.  
  380.         // call CharToPixel to get width of segment up to specified offset
  381.         width = CallWECharToPixelProc(pSegment, segmentLength,
  382.             slop, offset, direction, styleRunPosition, cd->thePoint->h,
  383.             hWE, pWE->charToPixelHook);
  384.     }
  385.  
  386.     // advance thePoint.h by the width of this segment
  387.     cd->thePoint->h += width;
  388.  
  389.     // drop out of loop when we reach offset
  390.     return isInSegment;
  391. }
  392.  
  393. pascal void WEGetPoint(SInt32 offset, SInt16 direction, LongPt *thePoint, SInt16 *lineHeight, WEHandle hWE)
  394. {
  395.     // given a byte offset into the text, find the corresponding glyph position
  396.     // this routine is useful for highlighting the text and for positioning the caret
  397.  
  398.     WEPtr pWE;
  399.     LineRec *pLine;
  400.     SInt32 lineIndex;
  401.     SInt32 height;
  402.     struct SLCharToPixelData cd;
  403.     Boolean saveWELock;
  404.  
  405.     // lock the WE record
  406.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  407.     pWE = *hWE;
  408.  
  409.     // the base point is the top left corner of the destination rectangle
  410.     *thePoint = * (LongPt *) &pWE->destRect;
  411.  
  412.     // first of all find the line on which the glyph lies
  413.     lineIndex = WEOffsetToLine(offset, hWE);
  414.  
  415.     // calculate the vertical coordinate and the line height
  416.     pLine = *pWE->hLines + lineIndex;
  417.     thePoint->v += pLine->lineOrigin;
  418.     height = pLine[1].lineOrigin - pLine[0].lineOrigin;
  419.  
  420.     if ((offset == pWE->textLength) && (WEGetChar(offset - 1, hWE) == kEOL))
  421.     {
  422.         // SPECIAL CASE: if offset is past the last character and
  423.         // the last character is a carriage return, return a point below the last line
  424.         LineRec dummyLine;
  425.  
  426.         dummyLine.lineSlop = pWE->destRect.right - pWE->destRect.left;
  427.         dummyLine.lineJustAmount = 0;
  428.  
  429.         thePoint->v += height;
  430.         thePoint->h += _WECalcPenIndent(&dummyLine, pWE->alignment, pWE->direction);
  431.     }
  432.     else
  433.     {
  434. // ・・・ハnew version of this: better boundary behavior
  435.         SInt32    lineStart, lineEnd;
  436.         Boolean    lineDir;
  437.  
  438.         // determine the dominant line direction
  439.         lineDir = IsRightToLeft(pWE->direction);
  440.  
  441.         // simplify direction so it's either leftCaret or rightCaret
  442.         if (direction == hilite)
  443.         {
  444.             direction = (lineDir ? rightCaret : leftCaret);
  445.         }
  446.  
  447.         // find ends of line so we can special-case them
  448.         WEGetLineRange(lineIndex, &lineStart, &lineEnd, hWE);
  449.         if (lineEnd < pWE->textLength || WEGetChar(lineEnd - 1, hWE) == kEOL)
  450.         {
  451.             --lineEnd;
  452.         }
  453.  
  454.         if ((offset == lineStart && !lineDir && direction == leftCaret)
  455.             || (offset == lineEnd && lineDir && direction == rightCaret)
  456.            )
  457.         {
  458.             // SPECIAL CASE: if caret should be at left end of line, don't call style run loop
  459.             thePoint->h += _WECalcPenIndent(pLine, pWE->alignment, pWE->direction);
  460.         }
  461.         else
  462.         {
  463.             cd.lineDir = lineDir;
  464.             // Inform SLCharToPixel if we want the right edge of the line
  465.             cd.rightEdge = ((offset == lineEnd && !lineDir && direction == leftCaret) ||
  466.                             (offset == lineStart && lineDir && direction == rightCaret));
  467.             cd.offset = offset;
  468.             cd.thePoint = thePoint;
  469.             cd.direction = direction;
  470.  
  471.             // to get the horizontal coordinate, walk through the style runs on this line
  472.             _WESegmentLoop(lineIndex, lineIndex, SLCharToPixel, &cd, hWE);
  473.         }
  474.  
  475. // ・・・ハreplaced with code above
  476. /*
  477.         cd.offset = offset;
  478.         cd.thePoint = thePoint;
  479.         cd.direction = direction;
  480.  
  481.         // to get the horizontal coordinate, walk through the style runs on this line
  482.         _WESegmentLoop(lineIndex, lineIndex, SLCharToPixel, &cd, hWE);
  483. */
  484.     }
  485.  
  486.     // pin the horizontal coordinate to the destination rectangle
  487.     thePoint->h = _WEPinInRange(thePoint->h, pWE->destRect.left, pWE->destRect.right);
  488.  
  489.     // unlock the WE record
  490.     _WESetHandleLock((Handle) hWE, saveWELock);
  491.  
  492.     // copy line height
  493.     if (lineHeight != nil)
  494.     {
  495.         *lineHeight = height;
  496.     }
  497. }
  498.  
  499. pascal void WEFindLine(SInt32 offset, WEEdge edge, SInt32 *lineStart, SInt32 *lineEnd, WEHandle hWE)
  500. {
  501. #pragma unused(edge)
  502.     WEPtr pWE = *hWE;
  503.     LineRec *pLine;
  504.  
  505.     pLine = *pWE->hLines + WEOffsetToLine(offset, hWE);
  506.     *lineStart = pLine[0].lineStart;
  507.     *lineEnd = pLine[1].lineStart;
  508. }
  509.  
  510. pascal void WEFindParagraph(SInt32 offset, WEEdge edge, SInt32 *parStart, SInt32 *parEnd, WEHandle hWE)
  511. {
  512. #pragma unused(edge)
  513.     WEPtr pWE = *hWE;
  514.     Ptr pText = *(pWE->hText);
  515.     LineRec *pLine;
  516.  
  517.     pLine = *pWE->hLines + WEOffsetToLine(offset, hWE);
  518.     while ((pLine->lineStart > 0) && (pText[pLine->lineStart - 1] != kEOL))
  519.     {
  520.         --pLine;
  521.     }
  522.     *parStart = pLine->lineStart;
  523.     pLine = *pWE->hLines + WEOffsetToLine(offset, hWE) + 1;
  524.     while ((pLine->lineStart < pWE->textLength) && (pText[pLine->lineStart - 1] != kEOL))
  525.     {
  526.         ++pLine;
  527.     }
  528.     *parEnd = pLine->lineStart;
  529. }
  530.  
  531. pascal ScriptCode _WEGetContext(SInt32 offset, SInt32 *contextStart, SInt32 *contextEnd,
  532.                         WEHandle hWE)
  533. {
  534.     // This function finds a range of characters ("context"), all belonging to the same script
  535.     // and centered around the specified offset.
  536.     // The function result is the ID of a font belonging to this script.
  537.     // Ideally, the context should consist of a whole script run, but in practice the returned
  538.     // context can be narrower, for performance and other reasons (see below)
  539.  
  540.     SInt32 index, saveIndex, saveRunEnd;
  541.     WERunInfo runInfo;
  542.     ScriptCode script1, script2;
  543.     SInt16 retval;
  544.  
  545.     if (BTST((*hWE)->flags, weFNonRoman))
  546.     {
  547.         // if more than one script is installed, limit the search of script run boundaries
  548.         // to a single line, for speed's sake
  549. //         ・・・ハparagraph, not line:
  550. //・・・    WEFindLine(offset, kLeadingEdge, contextStart, contextEnd, hWE);
  551.         WEFindParagraph(offset, kLeadingEdge, contextStart, contextEnd, hWE);
  552.  
  553.         // find the style run the specified offset is in
  554.         index = WEOffsetToRun(offset, hWE);
  555.         _WEGetIndStyle(index, &runInfo, hWE);
  556.  
  557.         // find the script code associated with this style run
  558.         script1 = FontToScript(runInfo.runAttrs.runStyle.tsFont);
  559.  
  560.         // the script code is returned as function result
  561.         retval = script1;
  562.  
  563.         // save index and runInfo.runEnd for the second while loop
  564.         saveIndex = index;
  565.         saveRunEnd = runInfo.runEnd;
  566.  
  567.         // walk backwards across style runs preceding offset, looking for a script run boundary
  568.         while (runInfo.runStart > *contextStart)
  569.         {
  570.             index--;
  571.             _WEGetIndStyle(index, &runInfo, hWE);
  572.             script2 = FontToScript(runInfo.runAttrs.runStyle.tsFont);
  573.             if (script1 != script2)
  574.             {
  575.                 *contextStart = runInfo.runEnd;
  576.                 break;
  577.             }
  578.         }
  579.  
  580.         // restore index and runInfo.runEnd
  581.         index = saveIndex;
  582.         runInfo.runEnd = saveRunEnd;
  583.  
  584.         // walk forward across style runs following offset, looking for a script run boundary
  585.         while (runInfo.runEnd < *contextEnd)
  586.         {
  587.             index++;
  588.             _WEGetIndStyle(index, &runInfo, hWE);
  589.             script2 = FontToScript(runInfo.runAttrs.runStyle.tsFont);
  590.             if (script1 != script2)
  591.             {
  592.                 *contextEnd = runInfo.runStart;
  593.                 break;
  594.             }
  595.         }
  596.     }
  597.     else
  598.     {
  599.         // only the Roman script is enabled: the whole text constitutes one script run
  600.         retval = smRoman;
  601.         *contextStart = 0;
  602.         *contextEnd = (*hWE)->textLength;
  603.     }
  604.  
  605.     // make sure the range identified by contextStart/contextEnd is contained within
  606.     // the 32K byte range centered around the specified offset
  607.     // the reason for this is that many Script Manager routines (e.g. FindWord and CharByte)
  608.     // only accept 16-bit offsets, rather than 32-bit offsets
  609.  
  610.     *contextStart = _WEPinInRange(*contextStart, offset - (SHRT_MAX / 2), offset);
  611.     *contextEnd = _WEPinInRange(*contextEnd, offset, offset + (SHRT_MAX / 2));
  612.  
  613.     return retval;
  614. }
  615.  
  616. pascal ScriptCode _WEGetRestrictedContext(SInt32 offset, SInt32 *contextStart, SInt32 *contextEnd,
  617.                         WEHandle hWE)
  618. {
  619.     // This function finds a range of characters ("context"), all belonging to the same script
  620.     // and centered around the specified offset.
  621.     // This function returns a script run subrange and is more efficient than
  622.     // _WEGetContext because it doesn't try to find the script boundaries accurately.
  623.  
  624.     WERunInfo runInfo;
  625.  
  626.     // just find the style run the specified offset is in
  627.     WEGetRunInfo(offset, &runInfo, hWE);
  628.     *contextStart = runInfo.runStart;
  629.     *contextEnd = runInfo.runEnd;
  630.  
  631.     // make sure the range identified by contextStart/contextEnd is contained within
  632.     // the 32K byte range centered around the specified offset
  633.     // the reason for this is that many Script Manager routines (e.g. FindWord and CharByte)
  634.     // only accept 16-bit offsets, rather than 32-bit offsets
  635.  
  636.     *contextStart = _WEPinInRange(*contextStart, offset - (SHRT_MAX / 2), offset);
  637.     *contextEnd = _WEPinInRange(*contextEnd, offset, offset + (SHRT_MAX / 2));
  638.  
  639.     return FontToScript(runInfo.runAttrs.runStyle.tsFont);
  640. }
  641.  
  642. pascal void WEFindWord(SInt32 offset, WEEdge edge, SInt32 *wordStart, SInt32 *wordEnd, WEHandle hWE)
  643. {
  644.     WEPtr pWE;
  645.     ScriptCode script;
  646.     SInt32 contextStart, contextEnd;
  647.     Handle hText;
  648.     OffsetTable wordBreaks;
  649.     Boolean saveTextLock, saveWELock;
  650.  
  651.     // lock the WE record
  652.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  653.     pWE = *hWE;
  654.  
  655.     // find a script context containing the specified offset
  656.     // (words cannot straddle script boundaries)
  657.     script = _WEGetContext(offset, &contextStart, &contextEnd, hWE);
  658.  
  659.     // lock the text
  660.     hText = pWE->hText;
  661.     saveTextLock = _WESetHandleLock(hText, true);
  662.  
  663.     // call the word break hook
  664.     CallWEWordBreakProc(*hText + contextStart, contextEnd - contextStart,
  665.         offset - contextStart, edge, wordBreaks, script, hWE, pWE->wordBreakHook);
  666.  
  667.     // unlock the text
  668.     _WESetHandleLock(hText, saveTextLock);
  669.  
  670.     // calculate wordStart and wordEnd relative to the beginning of the text
  671.     *wordStart = contextStart + wordBreaks[0].offFirst;
  672.     *wordEnd = contextStart + wordBreaks[0].offSecond;
  673. }
  674.  
  675. pascal SInt16 WECharByte(SInt32 offset, WEHandle hWE)
  676. {
  677.     WEPtr pWE;
  678.     Handle hText;
  679.     SInt32 contextStart, contextEnd;
  680.     ScriptCode script;
  681.     Boolean saveWELock, saveTextLock;
  682.     SInt16 retval = smSingleByte;
  683.  
  684.     // lock the WE record
  685.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  686.     pWE = *hWE;
  687.  
  688.     // do nothing unless there is at least one double-byte script system installed
  689.     // and make sure offset is within allowed bounds
  690.     if (BTST(pWE->flags, weFDoubleByte))
  691.     {
  692.         if ((offset >= 0) && (offset < pWE->textLength))
  693.         {
  694.  
  695.             // find a script context containing the specified offset
  696.             script = _WEGetRestrictedContext(offset, &contextStart, &contextEnd, hWE);
  697.  
  698.             // lock the text
  699.             hText = pWE->hText;
  700.             saveTextLock = _WESetHandleLock(hText, true);
  701.  
  702.             // pass the CharByte hook a pointer to the beginning of the style run
  703.             retval = CallWECharByteProc(*hText + contextStart,
  704.                 offset - contextStart, script, hWE, pWE->charByteHook);
  705.  
  706.             // unlock the text
  707.             _WESetHandleLock(hText, saveTextLock);
  708.         }
  709.     }
  710.  
  711.     // unlock the WE record
  712.     _WESetHandleLock((Handle) hWE, saveWELock);
  713.  
  714.     return retval;
  715. }
  716.  
  717. pascal SInt16 WECharType(SInt32 offset, WEHandle hWE)
  718. {
  719.     WEPtr pWE;
  720.     Handle hText;
  721.     SInt32 contextStart, contextEnd;
  722.     ScriptCode script;
  723.     Boolean saveWELock, saveTextLock;
  724.     SInt16 retval = 0;
  725.  
  726.     // lock the WE record
  727.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  728.     pWE = *hWE;
  729.  
  730.     // make sure offset is within allowed bounds
  731.     if ((offset >= 0) && (offset < pWE->textLength))
  732.     {
  733.  
  734.         // find a script context containing the specified offset
  735.         script = _WEGetRestrictedContext(offset, &contextStart, &contextEnd, hWE);
  736.  
  737.         // lock the text
  738.         hText = pWE->hText;
  739.         saveTextLock = _WESetHandleLock(hText, true);
  740.  
  741.         // pass the CharType hook a pointer to the beginning of the style run
  742.         retval = CallWECharTypeProc(*hText + contextStart,
  743.             offset - contextStart, script, hWE, pWE->charTypeHook);
  744.  
  745.         // unlock the text
  746.         _WESetHandleLock(hText, saveTextLock);
  747.     }
  748.  
  749.     // unlock the WE record
  750.     _WESetHandleLock((Handle) hWE, saveWELock);
  751.  
  752.     return retval;
  753. }
  754.  
  755. pascal void _WEGetCaretRect(SInt32 offset, SInt16 direction, Rect *caretRect, WEHandle hWE)
  756. {
  757.     LongPt thePoint;
  758.     SInt16 caretHeight;
  759.  
  760.     WEGetPoint(offset, direction, &thePoint, &caretHeight, hWE);
  761.     WELongPointToPoint(&thePoint, (Point *) &caretRect->top);
  762.     if (caretRect->left > (*hWE)->destRect.left)
  763.     {
  764.         caretRect->left--;
  765.     }
  766.  
  767.     caretRect->bottom = caretRect->top + caretHeight;
  768.     caretRect->right = caretRect->left + kCaretWidth;
  769. }
  770.  
  771. pascal void _WEDrawCaret(SInt32 offset, SInt16 direction, Boolean useDualCaret, WEHandle hWE)
  772. {
  773.     WEPtr pWE = *hWE;    // assume WE record is already locked
  774.     Rect caretRect;
  775.     GrafPtr savePort;
  776.     RgnHandle saveClip;
  777.  
  778.     // set up the port
  779.     GetPort(&savePort);
  780.     SetPort(pWE->port);
  781.  
  782.     // clip to the view region
  783.     saveClip = NewRgn();
  784.     GetClip(saveClip);
  785.     SetClip(pWE->viewRgn);
  786.  
  787.     // calculate caret rectangle for the primary caret
  788.     _WEGetCaretRect(offset, direction, &caretRect, hWE);
  789.  
  790.     // should we use a dual caret?
  791.     if (useDualCaret)
  792.     {
  793.         // draw the primary caret
  794.         caretRect.bottom = (caretRect.top + caretRect.bottom) >> 1;
  795.         InvertRect(&caretRect);
  796.  
  797.         // calculate the caret rectangle for the secondary caret
  798.         _WEGetCaretRect(offset, (leftCaret + rightCaret) - direction, &caretRect, hWE);
  799.         caretRect.top = (caretRect.top + caretRect.bottom) >> 1;
  800.     }
  801.  
  802.     // draw the caret (either a single caret, or the secondary caret)
  803.     InvertRect(&caretRect);
  804.  
  805.     // restore the clip region
  806.     SetClip(saveClip);
  807.     DisposeRgn(saveClip);
  808.  
  809.     // restore the port
  810.     SetPort(savePort);
  811. }
  812.  
  813. pascal void _WEBlinkCaret(WEHandle hWE)
  814. {
  815.     WEPtr pWE = *hWE;                // assume WE record is already locked
  816.     SInt16 direction = hilite;
  817.     Boolean useDualCaret = false;
  818.  
  819.     // do nothing if we're not active
  820.     if (!BTST(pWE->flags, weFActive))
  821.     {
  822.         return;
  823.     }
  824.  
  825. #if WASTE_NO_RO_CARET
  826.     if (BTST(pWE->features, weFReadOnly) && !BTST(pWE->flags, weFCaretVisible))
  827.     {
  828.         return;
  829.     }
  830. #endif
  831.  
  832.     if (BTST(pWE->flags, weFBidirectional))
  833.     {
  834.         // SPECIAL PROCESSING FOR BIDIRECTIONAL SCRIPTS
  835.         // check if we should use a dual caret
  836.         if (BTST(GetScriptManagerVariable(smGenFlags), smfDualCaret))
  837.         {
  838.             //    DUAL CARET
  839.             useDualCaret = true;
  840.  
  841.             //    primary caret position depends on primary line direction
  842.             direction = IsRightToLeft(pWE->direction) ? rightCaret : leftCaret;
  843.         }
  844.         else
  845.         {
  846.             //    SINGLE (JUMPING) CARET
  847.             //    caret position depends on keyscript direction
  848.             //    when erasing, though, use previously used direction
  849.             //    in case keyscript changed in the meantime
  850.             if (BTST(pWE->flags, weFCaretVisible))
  851.             {
  852.                 direction = BTST(pWE->flags, weFCaretRight) ? rightCaret : leftCaret;
  853.             }
  854.             else
  855.             {
  856.                 if (GetScriptVariable(GetScriptManagerVariable(smKeyScript), smScriptRight))
  857.                 {
  858.                     direction = rightCaret;
  859.                     BSET(pWE->flags, weFCaretRight);
  860.                 }
  861.                 else
  862.                 {
  863.                     direction = leftCaret;
  864.                     BCLR(pWE->flags, weFCaretRight);
  865.                 }
  866.             }
  867.         }
  868.     }
  869.  
  870.     // redraw the caret, in XOR mode
  871.     _WEDrawCaret(pWE->selStart, direction, useDualCaret, hWE);
  872.  
  873.     // keep track of the current caret visibility status
  874.     BCHG(pWE->flags, weFCaretVisible);    // invert flag
  875.  
  876.     // update caretTime
  877.     pWE->caretTime = TickCount();
  878. }
  879.  
  880. static Boolean SLCollectHiliteRgn(LinePtr pLine, const WERunAttributes *pAttrs,
  881.         Ptr pSegment, SInt32 segmentStart, SInt32 segmentLength,
  882.         JustStyleCode styleRunPosition, WEHandle hWE, void *callbackData)
  883. // Get the hilite rgn for the current run, or the portion thereof within the hilite range;
  884. // this just does FrameRect for the relevant area(s) -- caller is assumed to have a region open
  885. {
  886.     WEPtr pWE = *hWE;
  887.     struct SLCollectHiliteRgnData *cd = (struct SLCollectHiliteRgnData *) callbackData;
  888.     SInt16 lineHeight;
  889.     SInt32 segmentEnd;
  890.     SInt32 firstOffset, secondOffset;
  891.     OffsetTable    offsets;
  892.     SInt16 i;
  893.     Rect r;
  894.     Fixed slop;
  895.     SInt16 width;
  896.     SInt32 start, end, lineOrigin;
  897.     WEDirection direction;
  898.     SInt32 offset;
  899.  
  900.     // determine the line direction
  901.     direction = pWE->direction;
  902.     if (direction == weDirDefault)
  903.     {
  904.         direction = GetSysDirection();
  905.     }
  906.  
  907.     // find the line origin & height
  908.     lineOrigin = pWE->destRect.top + pLine->lineOrigin;
  909.     lineHeight = pLine[1].lineOrigin - pLine[0].lineOrigin;
  910.  
  911.     // initialize cd->hPos on first segment of the line
  912.     if (IS_LEFTMOST_RUN(styleRunPosition))
  913.     {
  914.         cd->hPos = pWE->destRect.left + _WECalcPenIndent(pLine, pWE->alignment, direction);
  915.  
  916.         // if selection starts before beginning of line AND direction is LR,
  917.         // OR selection ends after end of line AND direction is RL,
  918.         // then we need to include the area to the left of the line in the hilite
  919.  
  920.         if ( ((cd->rangeStart < pLine[0].lineStart) && (direction == weDirLeftToRight))
  921.           || ((cd->rangeEnd >= pLine[1].lineStart) && (direction == weDirRightToLeft)) )
  922.         {
  923.             // frame the rect to the left of the actual text
  924.             SetRect(&r, pWE->destRect.left, lineOrigin, cd->hPos, lineOrigin + lineHeight);
  925.             FrameRect(&r);
  926.         }
  927.     }
  928.  
  929.     // calculate segment end
  930.     segmentEnd = segmentStart + segmentLength;
  931.  
  932. #if WASTE_OBJECTS
  933.     if (pAttrs->runStyle.tsObject != nil)
  934.     {
  935.         // EMBEDDED OBJECT
  936.  
  937.         // get object width
  938.         width = (*pAttrs->runStyle.tsObject)->objectSize.h;
  939.  
  940.         // if this object is hilited, add its rect to the region
  941.         if (cd->rangeStart <= segmentStart && cd->rangeEnd >= segmentEnd)
  942.         {
  943.             SetRect(&r, cd->hPos, lineOrigin, cd->hPos + width, lineOrigin + lineHeight);
  944.             FrameRect(&r);
  945.         }
  946.     }
  947.     else
  948. #endif
  949.     {
  950.         // REGULAR TEXT
  951.  
  952.         // calculate slop value if justifying
  953.         if (pWE->alignment == weJustify)
  954.         {
  955.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition, kOneToOneScaling, kOneToOneScaling), pLine->lineJustAmount);
  956.         }
  957.         else
  958.         {
  959.             slop = 0;
  960.         }
  961.  
  962.         // see whether this run includes any hilited text
  963.         if (cd->rangeEnd > segmentStart && cd->rangeStart < segmentEnd)
  964.         {
  965.             if (segmentStart < cd->rangeStart)
  966.             {
  967.                 firstOffset = cd->rangeStart - segmentStart;
  968.             }
  969.             else
  970.             {
  971.                 firstOffset = 0;
  972.             }
  973.  
  974.             if (segmentEnd >= cd->rangeEnd)
  975.             {
  976.                 secondOffset = cd->rangeEnd - segmentStart;
  977.             }
  978.             else
  979.             {
  980.                 secondOffset = segmentLength;
  981.             }
  982.  
  983.             HiliteText(pSegment, segmentLength, firstOffset, secondOffset, offsets);
  984.  
  985.             for ( i = 0; i <= 2; ++i )
  986.             {    // for each pair of offsets...
  987.                 if (offsets[i].offFirst != offsets[i].offSecond)
  988.                 {    // if offset pair is not empty...
  989.                     // calculate horizontal coordinates of start and end of hilite area
  990.                     start = CallWECharToPixelProc(pSegment, segmentLength,
  991.                         slop, offsets[i].offFirst, hilite, styleRunPosition, cd->hPos,
  992.                         hWE, pWE->charToPixelHook);
  993.                     end = CallWECharToPixelProc(pSegment, segmentLength,
  994.                         slop, offsets[i].offSecond, hilite, styleRunPosition, cd->hPos,
  995.                         hWE, pWE->charToPixelHook);
  996.                     // frame the rectangle to be hilited
  997.                     SetRect(&r, cd->hPos + start, lineOrigin, cd->hPos + end, lineOrigin + lineHeight);
  998.                     FrameRect(&r);
  999.                 }    // if offset pair is not empty
  1000.             }    // for each pair of offsets
  1001.         }
  1002.  
  1003.         // calculate the width of this segment
  1004.         if (BTST(pAttrs->runStyle.tsFlags, tsRightToLeft) == 0)
  1005.         {
  1006.             i = leftCaret;
  1007.             offset = segmentLength;
  1008.         }
  1009.         else
  1010.         {
  1011.             i = rightCaret;
  1012.             offset = 0;
  1013.         }
  1014.         width = CallWECharToPixelProc(pSegment, segmentLength, slop,
  1015.             offset, i, styleRunPosition, cd->hPos, hWE, pWE->charToPixelHook);
  1016.     }
  1017.  
  1018.     // update cd->hPos to account for the width of the segment
  1019.     cd->hPos += width;
  1020.  
  1021.     if (IS_RIGHTMOST_RUN(styleRunPosition))
  1022.     {
  1023.         // if selection ends after end of line AND direction is LR,
  1024.         // OR selection begins before beginning of line AND direction is RL,
  1025.         // then we need to include the area to the right of the line in the hilite
  1026.  
  1027.         if ( (cd->rangeEnd >= pLine[1].lineStart && direction == weDirLeftToRight)
  1028.           || (cd->rangeStart < pLine->lineStart && direction == weDirRightToLeft) )
  1029.         {
  1030.             // frame the rect to the right of the text
  1031.             SetRect(&r, cd->hPos, lineOrigin, pWE->destRect.right, lineOrigin + lineHeight);
  1032.             FrameRect(&r);
  1033.         }
  1034.     }
  1035.  
  1036.     return false;    // don't break out of the segment loop
  1037. }
  1038.  
  1039. pascal RgnHandle WEGetHiliteRgn(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  1040. {
  1041.     // returns the hilite region corresponding to the specified range
  1042.     // the caller is responsible for disposing of the returned region
  1043.     // when it's finished with it
  1044.  
  1045.     WEPtr pWE;
  1046.     RgnHandle hiliteRgn;
  1047.     LongRect selRect;
  1048.     LongPt firstPoint, lastPoint;
  1049.     SInt32 lineIndex;
  1050.     SInt16 firstLineHeight, lastLineHeight;
  1051.     Rect r;
  1052.     GrafPtr savePort;
  1053.     Boolean saveWELock;
  1054.     Boolean bidirectional;
  1055.     struct SLCollectHiliteRgnData callbackData;
  1056.  
  1057.     // lock the WE record
  1058.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1059.     pWE = *hWE;
  1060.  
  1061.     // set up the port
  1062.     GetPort(&savePort);
  1063.     SetPort(pWE->port);
  1064.  
  1065.     // make sure rangeStart comes before rangeEnd
  1066.     _WEReorder(&rangeStart, &rangeEnd);
  1067.  
  1068.     // calculate pixel location corresponding to rangeStart
  1069.     WEGetPoint(rangeStart, hilite, &firstPoint, &firstLineHeight, hWE);
  1070.  
  1071.     // calculate pixel location corresponding to rangeEnd
  1072.     WEGetPoint(rangeEnd, hilite, &lastPoint, &lastLineHeight, hWE);
  1073.  
  1074.     // open a region: rects to be hilited will be accumulated in this
  1075.     OpenRgn();
  1076.  
  1077.     // any bidirectional scripts installed?
  1078.     bidirectional = (BTST(pWE->flags, weFBidirectional) != 0);
  1079.     if (bidirectional)
  1080.     {
  1081.         callbackData.rangeStart = rangeStart;
  1082.         callbackData.rangeEnd = rangeEnd;
  1083.     }
  1084.  
  1085.     if (firstPoint.v == lastPoint.v)
  1086.     {
  1087.         // selection range encompasses only one line
  1088.         // for bidirectional text, we need to loop through the style runs
  1089.         if (bidirectional)
  1090.         {
  1091.             lineIndex = WEOffsetToLine(rangeStart, hWE);
  1092.             _WESegmentLoop(lineIndex, lineIndex, SLCollectHiliteRgn, &callbackData, hWE);
  1093.         }
  1094.         else
  1095.         {
  1096.             WESetLongRect(&selRect, firstPoint.h, firstPoint.v, lastPoint.h, lastPoint.v + lastLineHeight);
  1097.             WELongRectToRect(&selRect, &r);
  1098.             FrameRect(&r);
  1099.         }
  1100.     }
  1101.     else
  1102.     {
  1103.         // selection range encompasses more than one line
  1104.         // hilite the first line
  1105.         if (bidirectional)
  1106.         {
  1107.             lineIndex = WEOffsetToLine(rangeStart, hWE);
  1108.             _WESegmentLoop(lineIndex, lineIndex, SLCollectHiliteRgn, &callbackData, hWE);
  1109.         }
  1110.         else
  1111.         {
  1112.             WESetLongRect(&selRect, firstPoint.h, firstPoint.v, pWE->destRect.right, firstPoint.v + firstLineHeight);
  1113.             WELongRectToRect(&selRect, &r);
  1114.             FrameRect(&r);
  1115.         }
  1116.  
  1117.         // any lines between the first and the last one?
  1118.         if (firstPoint.v + firstLineHeight < lastPoint.v)
  1119.         {
  1120.             // hilite all the lines in-between
  1121.             WESetLongRect(&selRect, pWE->destRect.left, firstPoint.v + firstLineHeight, pWE->destRect.right, lastPoint.v);
  1122.             WELongRectToRect(&selRect, &r);
  1123.             FrameRect(&r);
  1124.         }
  1125.  
  1126.         // hilite the last line
  1127.         if (bidirectional)
  1128.         {
  1129.             // ??? shouldn't this be WEOffsetToLine(rangeEnd - 1, hWE) ??? -- marco
  1130.             // ・・・ハyes, I think you're right, but I haven't tested it thoroughly -- jonathan
  1131.             lineIndex = WEOffsetToLine(rangeEnd, hWE);
  1132.  
  1133.             // skip this if it's an empty last line of the document
  1134.             if ((rangeEnd < pWE->textLength) || (WEGetChar(rangeEnd - 1, hWE) != kEOL))
  1135.             {
  1136.                 _WESegmentLoop(lineIndex, lineIndex, SLCollectHiliteRgn, &callbackData, hWE);
  1137.             }
  1138.         }
  1139.         else
  1140.         {
  1141.             WESetLongRect(&selRect, pWE->destRect.left, lastPoint.v, lastPoint.h, lastPoint.v + lastLineHeight);
  1142.             WELongRectToRect(&selRect, &r);
  1143.             FrameRect(&r);
  1144.         }
  1145.     }
  1146.  
  1147.     // copy the accumulated region into a new region
  1148.     hiliteRgn = NewRgn();
  1149.     CloseRgn(hiliteRgn);
  1150.  
  1151.     // restrict this region to the view region
  1152.     SectRgn(hiliteRgn, pWE->viewRgn, hiliteRgn);
  1153.  
  1154.     // restore the port
  1155.     SetPort(savePort);
  1156.  
  1157.     // unlock the WE record
  1158.     _WESetHandleLock((Handle) hWE, saveWELock);
  1159.  
  1160.     // return the hilite region
  1161.     return hiliteRgn;
  1162. }
  1163.  
  1164. pascal void _WEHiliteRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  1165. {
  1166.     WEPtr pWE;
  1167.     RgnHandle saveClip, auxRgn, hiliteRgn;
  1168.     PenState savePen;
  1169.     GrafPtr savePort;
  1170.  
  1171.     // the WE record must be already locked
  1172.     pWE = *hWE;
  1173.  
  1174.     // do nothing if the specified range is empty
  1175.     if (rangeStart == rangeEnd)
  1176.     {
  1177.         return;
  1178.     }
  1179.  
  1180.     // set up the port
  1181.     GetPort(&savePort);
  1182.     SetPort(pWE->port);
  1183.  
  1184.     // create auxiliary regions
  1185.     saveClip = NewRgn();
  1186.     auxRgn = NewRgn();
  1187.  
  1188.     // restrict the clip region to the view rectangle
  1189.     GetClip(saveClip);
  1190.     SectRgn(saveClip, pWE->viewRgn, auxRgn);
  1191.     SetClip(auxRgn);
  1192.  
  1193.     // get the hilite region corresponding to the specified range
  1194.     hiliteRgn = WEGetHiliteRgn(rangeStart, rangeEnd, hWE);
  1195.  
  1196.     // hilite the region or frame it, depending on the setting of the active flag
  1197.     if (BTST(pWE->flags, weFActive))
  1198.     {
  1199.         _WEClearHiliteBit();
  1200.         InvertRgn(hiliteRgn);
  1201.     }
  1202.     else if (BTST(pWE->features, weFOutlineHilite))
  1203.     {
  1204.         GetPenState(&savePen);
  1205.         PenNormal();
  1206.         PenMode(patXor);
  1207.         _WEClearHiliteBit();
  1208.         FrameRgn(hiliteRgn);
  1209.         SetPenState(&savePen);
  1210.     }
  1211.  
  1212.     // restore the clip region
  1213.     SetClip(saveClip);
  1214.  
  1215.     // dispose of all regions
  1216.     DisposeRgn(saveClip);
  1217.     DisposeRgn(auxRgn);
  1218.     DisposeRgn(hiliteRgn);
  1219.  
  1220.     // restore the port
  1221.     SetPort(savePort);
  1222. }
  1223.  
  1224. pascal void WESetSelection(SInt32 selStart, SInt32 selEnd, WEHandle hWE)
  1225. {
  1226.     WEPtr pWE;
  1227.     SInt32 oldSelStart, oldSelEnd;
  1228.     Boolean saveWELock;
  1229.  
  1230.     // lock the WE record
  1231.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1232.     pWE = *hWE;
  1233.  
  1234.     // range-check parameters
  1235.     selStart = _WEPinInRange(selStart, 0, pWE->textLength);
  1236.     selEnd = _WEPinInRange(selEnd, 0, pWE->textLength);
  1237.  
  1238.     // set the weFAnchorIsEnd bit if selStart > selEnd,  reorder the endpoints
  1239.     if (selStart > selEnd)
  1240.     {
  1241.         BSET(pWE->flags, weFAnchorIsEnd);
  1242.         _WEReorder(&selStart, &selEnd);
  1243.     }
  1244.     else
  1245.     {
  1246.         BCLR(pWE->flags, weFAnchorIsEnd);
  1247.     }
  1248.  
  1249.     // get old selection range
  1250.     oldSelStart = pWE->selStart;
  1251.     oldSelEnd = pWE->selEnd;
  1252.  
  1253.     // selection changed?
  1254.     if ((oldSelStart != selStart) || (oldSelEnd != selEnd))
  1255.     {
  1256.         // invalid the null style
  1257.         BCLR(pWE->flags, weFUseNullStyle);
  1258.  
  1259.         // hide the caret if it's showing
  1260.         if (BTST(pWE->flags, weFCaretVisible))
  1261.         {
  1262.             _WEBlinkCaret(hWE);
  1263.         }
  1264.  
  1265.         // set new selection range
  1266.         pWE->selStart = selStart;
  1267.         pWE->selEnd = selEnd;
  1268.  
  1269.         // skip this section if either recalc or redraw has been inhibited
  1270.         if (! (pWE->features & ((1L << weFInhibitRecal) | (1L << weFInhibitRedraw))))
  1271.         {
  1272.             // if we're active, invert the exclusive-OR between the old range and the new range.
  1273.             // if we're inactive, this optimization can't be used because of outline highlighting.
  1274.             if (BTST(pWE->flags, weFActive))
  1275.             {
  1276.                 _WEReorder(&oldSelStart, &selStart);
  1277.                 _WEReorder(&oldSelEnd, &selEnd);
  1278.                 _WEReorder(&oldSelEnd, &selStart);
  1279.             }
  1280.  
  1281.             _WEHiliteRange(oldSelStart, oldSelEnd, hWE);
  1282.             _WEHiliteRange(selStart, selEnd, hWE);
  1283.  
  1284.             if (!BTST(pWE->flags, weFMouseTracking))
  1285.             {
  1286.                 // redraw the caret immediately, if the selection range is empty
  1287.                 if (pWE->selStart == pWE->selEnd)
  1288.                 {
  1289.                     _WEBlinkCaret(hWE);
  1290.                 }
  1291.  
  1292.                 // clear clickCount, unless we're tracking the mouse
  1293.                 pWE->clickCount = 0;
  1294.  
  1295.                 // scroll the selection into view, unless we're tracking the mouse
  1296.                 WESelView(hWE);
  1297.  
  1298.             }
  1299.         } // if redrawing not inhibited
  1300.     } // if selection changed
  1301.  
  1302.     // unlock the WE record
  1303.     _WESetHandleLock((Handle) hWE, saveWELock);
  1304. }
  1305.  
  1306. static Boolean SLCrossDirectionBoundary(LinePtr pLine, const WERunAttributes *pAttrs,
  1307.         Ptr pSegment, SInt32 segmentStart, SInt32 segmentLength,
  1308.         JustStyleCode styleRunPosition, WEHandle hWE, void *callbackData)
  1309. {
  1310. #pragma unused(pLine, pAttrs, pSegment, hWE)
  1311.     struct SLCrossDirectionBoundaryData *cd = (struct SLCrossDirectionBoundaryData *) callbackData;
  1312.     Boolean isOldSegment = false;
  1313.  
  1314.     // have we reached the segment that contains the old offset?
  1315.     isOldSegment = (cd->oldOffset == segmentStart + segmentLength);
  1316.  
  1317.     if (isOldSegment)
  1318.     {
  1319.         if (cd->movingRight)
  1320.         {
  1321.             cd->isDone = true;    //    exit on next iteration
  1322.             return false;
  1323.         }
  1324.         else
  1325.         {
  1326.             if (IS_LEFTMOST_RUN(styleRunPosition))
  1327.             {
  1328.                 cd->newOffset = segmentStart;
  1329.             }
  1330.             return true;        //    exit now
  1331.         }
  1332.     }
  1333.  
  1334.     cd->newOffset = segmentStart + segmentLength;
  1335.  
  1336.     if ((! isOldSegment) && (cd->isDone))
  1337.     {
  1338.         //    we're in the segment to the right of old segment
  1339.         return true;
  1340.     }
  1341.  
  1342.     return false;    //    keep looping
  1343. }
  1344.  
  1345. pascal SInt32 _WECrossDirectionBoundary(SInt32 offset, Boolean movingRight, WEHandle hWE)
  1346. {
  1347.     struct SLCrossDirectionBoundaryData cd;
  1348.     SInt32 lineIndex = WEOffsetToLine(offset, hWE);
  1349.  
  1350.     cd.oldOffset = cd.newOffset = offset;
  1351.     cd.movingRight = movingRight;
  1352.     cd.isDone = false;
  1353.     _WESegmentLoop(lineIndex, lineIndex, SLCrossDirectionBoundary, &cd, hWE);
  1354.     return cd.newOffset;
  1355. }
  1356.  
  1357. pascal SInt32 _WEArrowOffset(SInt16 action, SInt32 offset, WEHandle hWE)
  1358. {
  1359.     // given an action code (corresponding to a modifiers + arrow key combo)
  1360.     // and an offset into the text, find the offset of the new caret position
  1361.  
  1362.     WEPtr pWE = *hWE;    //    assume the WE record is already locked
  1363.     LongPt thePoint;
  1364.     SInt32 rangeStart, rangeEnd;
  1365.     SInt16 lineHeight;
  1366.     SInt16 arrow;
  1367.     Boolean prevDir, nextDir;
  1368. #if WASTE_KURTHS_OPTION_ARROWS
  1369.     SInt16 cType;
  1370. #endif
  1371.  
  1372.     //    extract arrow portion from action parameter
  1373.     arrow = action & 0x0003;
  1374.  
  1375.     if (BTST(pWE->flags, weFBidirectional) && (arrow <= kGoRight))
  1376.     {
  1377.         //    bidirectional pre-processing for left/right arrow keys
  1378.         prevDir = WEGetRunDirection(offset - 1, hWE);
  1379.         nextDir = WEGetRunDirection(offset, hWE);
  1380.  
  1381.         //    determine whether offset is at a direction boundary
  1382.         if (prevDir != nextDir)
  1383.         {
  1384.             //    offset at direction boundary:
  1385.             //    determine whether we should cross the boundary
  1386.             if (prevDir != (arrow != kGoLeft))
  1387.             {
  1388.                 offset = _WECrossDirectionBoundary(offset, (arrow != kGoLeft), hWE);
  1389.             }
  1390.         } // at direction boundary
  1391.  
  1392.         if ((arrow == kGoLeft) ? prevDir : nextDir)
  1393.         {
  1394.             //    insertion point is within a RL run: invert left/right keys
  1395.             arrow = (kGoLeft + kGoRight) - arrow;
  1396.             action = (action & ~0x0003) | arrow;
  1397.         }
  1398.     }    //    bidirectional pre-processing
  1399.  
  1400.     switch (action)
  1401.     {
  1402.         case kGoLeft:
  1403.             if (offset > 0)
  1404.             {
  1405.                 offset--;
  1406.                 if (WECharByte(offset, hWE) != smSingleByte)
  1407.                 {
  1408.                     offset--;
  1409.                 }
  1410.             }
  1411.             break;
  1412.  
  1413.         case kGoRight:
  1414.             if (offset < pWE->textLength)
  1415.             {
  1416.                 if (WECharByte(offset, hWE) != smSingleByte)
  1417.                 {
  1418.                     offset++;
  1419.                 }
  1420.                 offset++;
  1421.             }
  1422.             break;
  1423.  
  1424.         case kGoUp:
  1425.             WEGetPoint(offset, hilite, &thePoint, nil, hWE);
  1426.             thePoint.v--;
  1427.             offset = WEGetOffset(&thePoint, nil, hWE);
  1428.             break;
  1429.  
  1430.         case kGoDown:
  1431.             WEGetPoint(offset, hilite, &thePoint, &lineHeight, hWE);
  1432.             thePoint.v += lineHeight;
  1433.             offset = WEGetOffset(&thePoint, nil, hWE);
  1434.             break;
  1435.  
  1436.         case kGoWordStart:
  1437. #if WASTE_KURTHS_OPTION_ARROWS
  1438.             // loop "forever" (until we break out of it)
  1439.             while (true)
  1440.             {
  1441.                 WEFindWord(offset, kTrailingEdge, &rangeStart, &rangeEnd, hWE);
  1442.                 offset = rangeStart;
  1443.  
  1444.                 // If the found range is empty, get outta here.  (Most
  1445.                 // likely this means that we have reached the beginning
  1446.                 // of the text.)
  1447.                 if (rangeStart == rangeEnd)
  1448.                     break;
  1449.  
  1450.                 cType = WECharType(rangeStart, hWE);
  1451.  
  1452.                 // If the char is punctuation (other than a number),
  1453.                 // it's not really a word, so keep looping.  Otherwise
  1454.                 // we're done.
  1455.                 if (((cType & smcTypeMask) != smCharPunct) ||
  1456.                     ((cType & smcClassMask) == smPunctNumber))
  1457.                 {
  1458.                     break;
  1459.                 }
  1460.             }
  1461. #else
  1462.             WEFindWord(offset, kTrailingEdge, &rangeStart, &rangeEnd, hWE);
  1463.             offset = rangeStart;
  1464. #endif // WASTE_KURTHS_OPTION_ARROWS
  1465.             break;
  1466.  
  1467.         case kGoWordEnd:
  1468. #if WASTE_KURTHS_OPTION_ARROWS
  1469.             // loop "forever" (until we break out of it)
  1470.             while (true)
  1471.             {
  1472.                 WEFindWord(offset, kLeadingEdge, &rangeStart, &rangeEnd, hWE);
  1473.                 offset = rangeEnd;
  1474.  
  1475.                 // If the found range is empty, get outta here.
  1476.                 // (Most likely this means that we have reached
  1477.                 // the end of the text.)
  1478.                 if (rangeStart == rangeEnd)
  1479.                     break;
  1480.  
  1481.                 // `rangeEnd - 1' may point in the middle of a two-byte
  1482.                 // character; that's ok, CharType can deal with that.
  1483.                 cType = WECharType(rangeEnd - 1, hWE);
  1484.  
  1485.                 // If the char is punctuation (other than a number),
  1486.                 // it's not really a word, so keep looping.  Otherwise
  1487.                 // we're done.
  1488.                 if (((cType & smcTypeMask) != smCharPunct) ||
  1489.                     ((cType & smcClassMask) == smPunctNumber))
  1490.                 {
  1491.                     break;
  1492.                 }
  1493.             }
  1494. #else
  1495.             WEFindWord(offset, kLeadingEdge, &rangeStart, &rangeEnd, hWE);
  1496.             offset = rangeEnd;
  1497. #endif // WASTE_KURTHS_OPTION_ARROWS
  1498.             break;
  1499.  
  1500.         case kGoTextStart:
  1501.             offset = 0;
  1502.             break;
  1503.  
  1504.         case kGoTextEnd:
  1505.             offset = pWE->textLength;
  1506.             break;
  1507.  
  1508.         case kGoLineStart:
  1509.             WEFindLine(offset, kLeadingEdge, &rangeStart, &rangeEnd, hWE);
  1510.             offset = rangeStart;
  1511.             break;
  1512.  
  1513.         case kGoLineEnd:
  1514.             WEFindLine(offset, kTrailingEdge, &rangeStart, &rangeEnd, hWE);
  1515.             offset = rangeEnd;
  1516.             if (offset < pWE->textLength)
  1517.             {
  1518.                 offset--;
  1519.                 if (WECharByte(offset, hWE) != smSingleByte)
  1520.                 {
  1521.                     offset--;
  1522.                 }
  1523.             }
  1524.             break;
  1525.  
  1526.         default:
  1527.             break;
  1528.     }
  1529.  
  1530.     if (BTST(pWE->flags, weFBidirectional) && (arrow <= kGoRight))
  1531.     {
  1532.         //    bidirectional post-processing for left/right arrow keys
  1533.         prevDir = WEGetRunDirection(offset - 1, hWE);
  1534.         nextDir = WEGetRunDirection(offset, hWE);
  1535.  
  1536.         //    determine whether offset is at a direction boundary
  1537.         //    and whether we should cross it
  1538.         if ((prevDir != nextDir) && (prevDir != (arrow == kGoLeft)))
  1539.         {
  1540.             offset = _WECrossDirectionBoundary(offset, (arrow == kGoLeft), hWE);
  1541.         }
  1542.     } // bidirectional post-processing
  1543.  
  1544.     return offset;
  1545. }
  1546.  
  1547. pascal void _WEDoArrowKey (SInt16 arrow, EventModifiers modifiers, WEHandle hWE)
  1548. {
  1549.     // this routine is called by WEKey to handle arrow keys
  1550.  
  1551.     WEPtr pWE = *hWE;    // assume the WE record is already locked
  1552.     SInt16 action;
  1553.     SInt32 selStart, selEnd;
  1554.     SInt32 caretLoc, anchor;
  1555.  
  1556.     // calculate the "action" parameter for _WEArrowOffset from arrow and modifiers
  1557.     action = arrow - kArrowLeft;            // possible range: 0..3
  1558.     if (modifiers & optionKey)
  1559.     {
  1560.         action += kOption;
  1561.     }
  1562.     if (modifiers & cmdKey)
  1563.     {
  1564.         action += kCommand;
  1565.     }
  1566.  
  1567.     // get selection range
  1568.     selStart = pWE->selStart;
  1569.     selEnd = pWE->selEnd;
  1570.  
  1571.     if ((modifiers & shiftKey) == 0)
  1572.     {
  1573.         // if selection range isn't empty, collapse it to one of the endpoints
  1574.         if (selStart < selEnd)
  1575.         {
  1576.             if ((arrow == kArrowLeft) || (arrow == kArrowUp))
  1577.             {
  1578.                 caretLoc = selStart;
  1579.             }
  1580.             else
  1581.             {
  1582.                 caretLoc = selEnd;
  1583.             }
  1584.         }
  1585.         else
  1586.         {
  1587.             // otherwise move the insertion point
  1588.             caretLoc = _WEArrowOffset(action, selStart, hWE);
  1589.         }
  1590.  
  1591.         // set anchor to caretLoc, so new selection will be empty
  1592.         anchor = caretLoc;
  1593.     }
  1594.     else
  1595.     {
  1596.         // shift key was held down: extend the selection rather than replacing it
  1597.         // find out which selection boundary is the anchor and which is the free endpoint
  1598.         if (BTST(pWE->flags, weFAnchorIsEnd))
  1599.         {
  1600.             anchor = selEnd;
  1601.             caretLoc = selStart;
  1602.         }
  1603.         else
  1604.         {
  1605.             anchor = selStart;
  1606.             caretLoc = selEnd;
  1607.         }
  1608.  
  1609.         // move the free endpoint
  1610.         caretLoc = _WEArrowOffset(action, caretLoc, hWE);
  1611.     }
  1612.  
  1613.     // select the new selection
  1614.     WESetSelection(anchor, caretLoc, hWE);
  1615. }
  1616.  
  1617. pascal Boolean WEAdjustCursor(Point mouseLoc, RgnHandle mouseRgn, WEHandle hWE)
  1618. {
  1619.     // Call WEAdjustCursor to set the cursor shape when the mouse is in the view rectangle.
  1620.     // MouseRgn should be either a valid region handle or nil.
  1621.     // If mouseRgn is supplied (i.e., if it's not nil), it is intersected with a region
  1622.     // in global coordinates within which the cursor is to retain its shape.
  1623.     // WEAdjustCursor returns true if the cursor has been set.
  1624.     // Your application should set the cursor only if WEAdjustCursor returns false.
  1625.  
  1626.     WEPtr pWE;
  1627.     RgnHandle auxRgn, hiliteRgn;
  1628.     enum { kIBeam, kArrow} cursorType;
  1629.     Point portDelta;
  1630.     GrafPtr savePort;
  1631.     Boolean saveWELock;
  1632.     Boolean adjustCursor;
  1633.  
  1634.     adjustCursor = false;
  1635.     cursorType = kIBeam;
  1636.  
  1637.     // lock the WE record
  1638.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1639.     pWE = *hWE;
  1640.  
  1641.     // set up the port
  1642.     GetPort(&savePort);
  1643.     SetPort(pWE->port);
  1644.  
  1645.     // calculate delta between the local coordinate system and the global one
  1646.     portDelta.v = 0;
  1647.     portDelta.h = 0;
  1648.     LocalToGlobal(&portDelta);
  1649.  
  1650.     // calculate the visible portion of the view rectangle, in global coordinates
  1651.     auxRgn = NewRgn();
  1652.     CopyRgn(pWE->viewRgn, auxRgn);
  1653.     SectRgn(auxRgn, pWE->port->visRgn, auxRgn);
  1654.     OffsetRgn(auxRgn, portDelta.h, portDelta.v);
  1655.  
  1656.     if (PtInRgn(mouseLoc, auxRgn))
  1657.     {
  1658.         // mouse is within view rectangle: it's up to us to set the cursor
  1659.         adjustCursor = true;
  1660.  
  1661.         // if drag-and-drop is enabled, see if the mouse is within current selection
  1662.         if (BTST(pWE->flags, weFHasDragManager) && BTST(pWE->features, weFDragAndDrop))
  1663.         {
  1664.             if (pWE->selStart < pWE->selEnd)
  1665.             {
  1666.  
  1667.                 // get current hilite region in global coordinates
  1668.                 hiliteRgn = WEGetHiliteRgn(pWE->selStart, pWE->selEnd, hWE);
  1669.                 OffsetRgn(hiliteRgn, portDelta.h, portDelta.v);
  1670.  
  1671.                 // if mouse is within selection, set cursor to an arrow, else to an I-beam
  1672.                 // (actually, we still use an I-beam if less than GetDoubleClickTime() ticks have
  1673.                 // elapsed since the last mouse click, so that the cursor doesn't turn into an
  1674.                 // arrow while triple-clicking + dragging a range of lines)
  1675.  
  1676.                 if (PtInRgn(mouseLoc, hiliteRgn) && ((TickCount() > pWE->clickTime + GetDoubleClickTime()) ||
  1677.                     (pWE->clickEdge == kObjectEdge)))
  1678.                 {
  1679.                     cursorType = kArrow;                // use arrow cursor
  1680.                     CopyRgn(hiliteRgn, auxRgn);
  1681.                 }
  1682.                 else
  1683.                 {
  1684.                     DiffRgn(auxRgn, hiliteRgn, auxRgn);
  1685.                 }
  1686.  
  1687.                 // dispose of the hilite region
  1688.                 DisposeRgn(hiliteRgn);
  1689.  
  1690.             } // if drag-and-drop is enabled
  1691.         }
  1692.  
  1693.         // set the cursor
  1694.         if (cursorType == kIBeam)
  1695.             SetCursor(*GetCursor(iBeamCursor));
  1696.         else
  1697.             SetCursor(&qd.arrow);
  1698.  
  1699.         // set mouseRgn, if provided
  1700.         if (mouseRgn != nil)
  1701.         {
  1702.             SectRgn(mouseRgn, auxRgn, mouseRgn);
  1703.         }
  1704.     }
  1705.     else
  1706.     {
  1707.         // mouse is outside view rectangle: don't set the cursor; subtract viewRgn from mouseRgn
  1708.         if (mouseRgn != nil)
  1709.         {
  1710.             DiffRgn(mouseRgn, auxRgn, mouseRgn);
  1711.         }
  1712.     }
  1713.     // dispose of the temporary region
  1714.     DisposeRgn(auxRgn);
  1715.  
  1716.     // restore the port
  1717.     SetPort(savePort);
  1718.  
  1719.     // unlock the WE record
  1720.     _WESetHandleLock((Handle) hWE, saveWELock);
  1721.  
  1722.     return adjustCursor;
  1723. }
  1724.  
  1725. pascal void WEIdle(UInt32 *maxSleep, WEHandle hWE)
  1726. {
  1727.     WEPtr pWE;
  1728.     UInt32 currentTime, blinkTime, sleep;
  1729.     Boolean saveWELock;
  1730.  
  1731.     // lock the WE record
  1732.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1733.     pWE = *hWE;
  1734.  
  1735. #if WASTE_DEBUG
  1736.         _WESanityCheck(hWE);
  1737. #endif
  1738.  
  1739.     // the caret blinks only if we're active and the selection point is empty
  1740.     if (BTST(pWE->flags, weFActive) && (pWE->selStart == pWE->selEnd))
  1741.     {
  1742.         // get current time
  1743.         currentTime = TickCount();
  1744.  
  1745.         // calculate when the caret should be blinked again
  1746.         blinkTime = pWE->caretTime + GetCaretTime();
  1747.  
  1748.         if (currentTime < blinkTime)
  1749.         {
  1750.             sleep = blinkTime - currentTime;
  1751.         }
  1752.         else
  1753.         {
  1754.             _WEBlinkCaret(hWE);
  1755.             sleep = GetCaretTime();
  1756.         }
  1757.     }
  1758.     else
  1759.     {
  1760.         // if we don't need to blink the caret, we can sleep forever
  1761.         sleep = LONG_MAX;
  1762.     }
  1763.  
  1764.     // return sleepTime to the caller if maxSleep isn't nil
  1765.     if (maxSleep != nil)
  1766.     {
  1767.         *maxSleep = sleep;
  1768.     }
  1769.  
  1770.     // unlock the WE record
  1771.     _WESetHandleLock((Handle) hWE, saveWELock);
  1772. }
  1773.  
  1774. pascal void WEUpdate(RgnHandle updateRgn, WEHandle hWE)
  1775. {
  1776.     WEPtr pWE;
  1777.     LongRect updateRect;
  1778.     Rect r;
  1779.     RgnHandle saveClip, auxRgn;
  1780.     GrafPtr savePort;
  1781.     Boolean saveWELock;
  1782.  
  1783.     // lock the WE record
  1784.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1785.     pWE = *hWE;
  1786.  
  1787.     // set up the port
  1788.     GetPort(&savePort);
  1789.     SetPort(pWE->port);
  1790.  
  1791.     // save the clip region
  1792.     saveClip = NewRgn();
  1793.     GetClip(saveClip);
  1794.  
  1795.     // clip to the insersection between updateRgn and the view rectangle
  1796.     // (updateRgn may be nil; in this case, just clip to the view rectangle)
  1797.     auxRgn = NewRgn();
  1798.     if (updateRgn != nil)
  1799.     {
  1800.         SectRgn(updateRgn, pWE->viewRgn, auxRgn);
  1801.     }
  1802.     else
  1803.     {
  1804.         CopyRgn(pWE->viewRgn, auxRgn);
  1805.     }
  1806.     SetClip(auxRgn);
  1807.  
  1808.     if (!EmptyRgn(auxRgn))
  1809.     {
  1810.         // calculate the rectangle to update
  1811.         r = (*auxRgn)->rgnBBox;
  1812.         WERectToLongRect(&r, &updateRect);
  1813.  
  1814.         // find out which lines need to be redrawn and draw them
  1815.         // if updateRgn is nil, erase each line rectangle before redrawing
  1816.         _WEDrawLines( _WEPixelToLine(updateRect.top - pWE->destRect.top, hWE),
  1817.                       _WEPixelToLine((updateRect.bottom - 1) - pWE->destRect.top, hWE),
  1818.                       (updateRgn == nil), hWE);
  1819.  
  1820.         // erase the portion of the update rectangle below the last line (if any)
  1821.         updateRect.top = pWE->destRect.top + (*pWE->hLines)[pWE->nLines].lineOrigin;
  1822.         if (updateRect.top < updateRect.bottom)
  1823.         {
  1824.             WELongRectToRect(&updateRect, &r);
  1825.             CallWEEraseProc(&r, hWE, pWE->eraseHook);
  1826.         }
  1827.  
  1828.         // hilite the selection range or draw the caret (only if active)
  1829.         if (pWE->selStart < pWE->selEnd)
  1830.         {
  1831.             _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1832.         }
  1833.         else if (BTST(pWE->flags, weFCaretVisible))
  1834.         {
  1835.             _WEBlinkCaret(hWE);
  1836.             BSET(pWE->flags, weFCaretVisible);
  1837.         }
  1838.     }
  1839.  
  1840.     DisposeRgn(auxRgn);
  1841.  
  1842.     // restore the clip region
  1843.     SetClip(saveClip);
  1844.     DisposeRgn(saveClip);
  1845.  
  1846.     // restore the port
  1847.     SetPort(savePort);
  1848.  
  1849.     // unlock the WE record
  1850.     _WESetHandleLock((Handle) hWE, saveWELock);
  1851. }
  1852.  
  1853. pascal void WEDeactivate(WEHandle hWE)
  1854. {
  1855.     WEPtr pWE;
  1856.     Boolean saveWELock;
  1857.  
  1858.     // lock the WE record
  1859.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1860.     pWE = *hWE;
  1861.  
  1862.     // do nothing if we are already inactive
  1863.     if (BTST(pWE->flags, weFActive))
  1864.     {
  1865.  
  1866.         // hide the selection range or the caret
  1867.         _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1868.         if (BTST(pWE->flags, weFCaretVisible))
  1869.             _WEBlinkCaret(hWE);
  1870.  
  1871.         // clear the active flag
  1872.         BCLR(pWE->flags, weFActive);
  1873.  
  1874.         // frame the selection
  1875.         _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1876.  
  1877.         // dispose of the offscreen graphics world, if any
  1878.         if (pWE->offscreenPort != nil)
  1879.         {
  1880.             DisposeGWorld((GWorldPtr)(pWE->offscreenPort));
  1881.             pWE->offscreenPort = nil;
  1882.         }
  1883.  
  1884.         // notify Text Services
  1885.         if (pWE->tsmReference != nil)
  1886.             DeactivateTSMDocument(pWE->tsmReference);
  1887.     }
  1888.  
  1889.     // unlock the WE record
  1890.     _WESetHandleLock((Handle) hWE, saveWELock);
  1891. }
  1892.  
  1893. pascal void WEActivate(WEHandle hWE)
  1894. {
  1895.     WEPtr pWE;
  1896.     Boolean saveWELock;
  1897.  
  1898.     if (WEIsActive(hWE)) return;
  1899.  
  1900.     // lock the WE record
  1901.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1902.     pWE = *hWE;
  1903.  
  1904.     // remove the selection frame
  1905.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1906.  
  1907.     // set the active flag
  1908.     BSET(pWE->flags, weFActive);
  1909.  
  1910.     // show the selection range
  1911.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1912.  
  1913.     // notify Text Services
  1914.     if (pWE->tsmReference != nil)
  1915.     {
  1916.         ActivateTSMDocument(pWE->tsmReference);
  1917.     }
  1918.  
  1919.     // unlock the WE record
  1920.     _WESetHandleLock((Handle) hWE, saveWELock);
  1921. }
  1922.  
  1923. pascal Boolean WEIsActive(WEHandle hWE)
  1924. {
  1925.     // return true iff the specified WE instance is currently active
  1926.     return BTST((*hWE)->flags, weFActive) ? true : false;
  1927. }
  1928.  
  1929. pascal void WEScroll(SInt32 hOffset, SInt32 vOffset, WEHandle hWE)
  1930. {
  1931.     WEPtr pWE;
  1932.     Rect viewRect;
  1933.     GrafPtr savePort;
  1934.     Boolean hideOutline, saveWELock;
  1935.  
  1936.     // do nothing if both scroll offsets are zero
  1937.     if ((hOffset == 0) && (vOffset == 0))
  1938.         return;
  1939.  
  1940.     // lock the WE record
  1941.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1942.     pWE = *hWE;
  1943.  
  1944.     // set up the port
  1945.     GetPort(&savePort);
  1946.     SetPort(pWE->port);
  1947.  
  1948.     // get view rect in short coordinates
  1949.     viewRect = (*pWE->viewRgn)->rgnBBox;
  1950.  
  1951.     // hide the caret if it's showing
  1952.     if (BTST(pWE->flags, weFCaretVisible))
  1953.     {
  1954.         _WEBlinkCaret(hWE);
  1955.     }
  1956.  
  1957.     // if we're inactive and outline highlighting is on, we have to temporarily
  1958.     // hide the selection outline while scrolling to avoid a cosmetic bug
  1959.     hideOutline = false;
  1960.     if (!BTST(pWE->flags, weFActive))
  1961.         if (BTST(pWE->features, weFOutlineHilite))
  1962.         {
  1963.             hideOutline = true;
  1964.             _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1965.             BCLR(pWE->features, weFOutlineHilite);
  1966.         }
  1967.  
  1968.     // offset the destination rectangle by the specified amount
  1969.     WEOffsetLongRect(&pWE->destRect, hOffset, vOffset);
  1970.  
  1971.     // scroll the view rectangle
  1972.     // we use ScrollRect unless the whole text is to be redrawn anyway
  1973.     // notice that both ScrollRect and DragPreScroll take short (16-bit)
  1974.     // offset parameters, while WEScroll deals with long (32-bit) quantities
  1975.     if ((ABS(hOffset) < (viewRect.right - viewRect.left)) && (ABS(vOffset) < (viewRect.bottom - viewRect.top)))
  1976.     {
  1977.         RgnHandle updateRgn = NewRgn();
  1978.  
  1979.         // if we're currently tracking a drag, notify the Drag Manager we're about to scroll
  1980.         if (pWE->currentDrag != kNullDrag)
  1981.             DragPreScroll(pWE->currentDrag, (SInt16) hOffset, (SInt16) vOffset);
  1982.  
  1983.         // ScrollRect will set updateRgn to the region to redraw
  1984.         ScrollRect(&viewRect, (SInt16) hOffset, (SInt16) vOffset, updateRgn);
  1985.  
  1986.         if (pWE->currentDrag != kNullDrag)
  1987.             DragPostScroll(pWE->currentDrag);
  1988.  
  1989.         // redraw the exposed region
  1990.         WEUpdate(updateRgn, hWE);
  1991.         DisposeRgn(updateRgn);
  1992.     }
  1993.     else
  1994.     {
  1995.         // redraw the whole text
  1996.         WEUpdate(nil, hWE);
  1997.     }
  1998.  
  1999.     // redraw the selection outline, if hidden
  2000.     if (hideOutline)
  2001.     {
  2002.         BSET(pWE->features, weFOutlineHilite);
  2003.         _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  2004.     }
  2005.  
  2006.     // restore the port
  2007.     SetPort(savePort);
  2008.  
  2009.     // unlock the WE record
  2010.     _WESetHandleLock((Handle) hWE, saveWELock);
  2011. }
  2012.  
  2013. pascal void WEPinScroll(SInt32 hOffset, SInt32 vOffset, WEHandle hWE)
  2014. {
  2015.     WEPtr pWE = *hWE;    // we ain't gonna move memory
  2016.     SInt32 delta;
  2017.  
  2018.     if (vOffset > 0)
  2019.     {
  2020.         delta = pWE->viewRect.top - pWE->destRect.top;
  2021.  
  2022.         // if top of the destRect would be moved below top of the viewRect,
  2023.         // pin it to top of the viewRect
  2024.         if (vOffset > delta)
  2025.         {
  2026.             vOffset = delta;
  2027.         }
  2028.     }
  2029.     else if (vOffset < 0)
  2030.     {
  2031.         delta = pWE->viewRect.bottom - pWE->destRect.bottom;
  2032.  
  2033.         // if bottom of the destRect would be moved above bottom of the viewRect,
  2034.         // pin it to bottom of viewRect
  2035.         if (vOffset < delta)
  2036.         {
  2037.             vOffset = delta;
  2038.         }
  2039.     }
  2040.  
  2041.     WEScroll(hOffset, vOffset, hWE);
  2042. }
  2043.  
  2044. pascal Boolean _WEScrollIntoView (SInt32 offset, WEHandle hWE)
  2045. {
  2046.     WEPtr pWE = *hWE;
  2047.     LongPt thePoint;
  2048.     SInt16 lineHeight;
  2049.     SInt32 hScroll, vScroll, temp;
  2050.     Boolean retval;
  2051.  
  2052.     // do nothing if automatic scrolling is disabled
  2053.     if (!BTST(pWE->features, weFAutoScroll))
  2054.     {
  2055.         return false;
  2056.     }
  2057.  
  2058.     // find the selection point
  2059.     WEGetPoint(offset, hilite, &thePoint, &lineHeight, hWE);
  2060.  
  2061.     // assume no scrolling is needed
  2062.     retval = false;
  2063.     vScroll = 0;
  2064.     hScroll = 0;
  2065.  
  2066.     // determine if we need to scroll vertically
  2067.     if ((thePoint.v < pWE->viewRect.top) ||
  2068.         (thePoint.v + lineHeight >= pWE->viewRect.bottom))
  2069.     {
  2070.         // calculate the amount of vertical scrolling needed to center the selection into view
  2071.         vScroll = ((pWE->viewRect.top + pWE->viewRect.bottom) >> 1) -
  2072.                     (thePoint.v + (lineHeight >> 1));
  2073.  
  2074.         // we'd like to superimpose the bottom margins of the dest/view rects, if possible
  2075.         temp = pWE->viewRect.bottom - pWE->destRect.bottom;
  2076.         if (temp > vScroll)
  2077.         {
  2078.             vScroll = temp;
  2079.         }
  2080.         // but we also have to make sure the dest top isn't scrolled below the view top
  2081.         temp = pWE->viewRect.top - pWE->destRect.top;
  2082.         if (temp < vScroll)
  2083.         {
  2084.             vScroll = temp;
  2085.         }
  2086.     }
  2087.  
  2088.     // determine if we need to scroll horizontally
  2089.     if ((thePoint.h - 1 < pWE->viewRect.left) || (thePoint.h >= pWE->viewRect.right))
  2090.     {
  2091.         // calculate the amount of horizontal scrolling needed to center the selection into view
  2092.         hScroll = ((pWE->viewRect.left + pWE->viewRect.right) >> 1) - thePoint.h;
  2093.  
  2094.         // we'd like to superimpose the right margins of the dest/view rects, if possible
  2095.         temp = pWE->viewRect.right - pWE->destRect.right;
  2096.         if (temp > hScroll)
  2097.         {
  2098.             hScroll = temp;
  2099.         }
  2100.  
  2101.         // but we also have to make sure the dest left isn't scrolled to the right of the view left
  2102.         temp = pWE->viewRect.left - pWE->destRect.left;
  2103.         if (temp < hScroll)
  2104.         {
  2105.             hScroll = temp;
  2106.         }
  2107.     }
  2108.  
  2109.     // scroll the text if necessary
  2110.     if ((vScroll != 0) || (hScroll != 0))
  2111.     {
  2112.         retval = true;
  2113.         WEScroll(hScroll, vScroll, hWE);
  2114.         BSET(pWE->flags, weFDestRectChanged);
  2115.     }
  2116.  
  2117.     // notify our client of changes to the destination rectangle
  2118.     if (BTST(pWE->flags, weFDestRectChanged))
  2119.     {
  2120.         if (pWE->scrollProc != nil)
  2121.         {
  2122.             CallWEScrollProc(hWE, pWE->scrollProc);
  2123.         }
  2124.         BCLR(pWE->flags, weFDestRectChanged);
  2125.     }
  2126.  
  2127.     // call the scroll callback, if any
  2128.     return retval;
  2129. }
  2130.  
  2131. pascal void WESelView(WEHandle hWE)
  2132. {
  2133.     WEPtr pWE;
  2134.     Boolean saveWELock;
  2135.  
  2136.     // lock the WE record
  2137.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  2138.     pWE = *hWE;
  2139.  
  2140.     // scroll the free endpoint of the selection into view
  2141.     _WEScrollIntoView(BTST(pWE->flags, weFAnchorIsEnd) ? pWE->selStart : pWE->selEnd, hWE);
  2142.  
  2143.     // unlock the WE record
  2144.     _WESetHandleLock((Handle) hWE, saveWELock);
  2145. }
  2146.